%%% -------------------------------------------------------------------
%%% Author  : 77
%%% Description :
%%%
%%% Created : 2011-7-2
%%% -------------------------------------------------------------------
-module(meta_server).

-author(kanghua ).  
-email(kanghua151@msn.com).  
-date("2011.07.06").  


-behaviour(gen_server).
%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------
%%-include("meta_schema.hrl").
%%-include("new.hrl").

%% --------------------------------------------------------------------
%% External exports
-export([start_link/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

-record(state, {
               self,                       %% self pid
			   nodeup_monitor_info_list    %% mnesia table name
			   }).

%% ====================================================================
%% External functions
%% ====================================================================
start_link() ->
	io:format("----- start meta server -------~n"), 
    gen_server:start_link({local,?MODULE}, ?MODULE, [], []).

%% ====================================================================
%% Server functions
%% ====================================================================

%% --------------------------------------------------------------------
%% Function: init/1
%% Description: Initiates the server
%% Returns: {ok, State}          |
%%          {ok, State, Timeout} |
%%          ignore               |
%%          {stop, Reason}
%% --------------------------------------------------------------------
init([]) ->
	io:format("----- meta server init  ------- ~n") , 
	process_flag(trap_exit,true),  
	monitor_nodes(),          %% monitor node down or node up
	init_mnesia(),            %% wait for sync mneisa
	%% read node info from resource table.
	%% start all node.
    {ok, #state{
				self=self(),
				nodeup_monitor_info_list=[]%nodeup_monitor_dict = dict:new()
			    }}.

%% --------------------------------------------------------------------
%% Function: handle_call/3
%% Description: Handling call messages
%% Returns: {reply, Reply, State}          |
%%          {reply, Reply, State, Timeout} |
%%          {noreply, State}               |
%%          {noreply, State, Timeout}      |
%%          {stop, Reason, Reply, State}   | (terminate/2 is called)
%%          {stop, Reason, State}            (terminate/2 is called)
%% --------------------------------------------------------------------
handle_call({add_node,Content}, From, State) ->
	io:format("Recieve add_node Req ~p ~p ~p ~n",[Content,From,State] ), %% debug info
	_Pid = spawn(add_node_cmd,handle,[Content,From,State]),
    io:format("process pid :~p ~n",[_Pid]), %% debug info
	{noreply, State};
handle_call({drop_node,Content}, From, State) ->
	io:format("Recieve add_node Req ~p ~p ~p ~n",[Content,From,State] ), %% debug info
	_Pid = spawn(drop_node_cmd,handle,[Content,From,State]),
    io:format("process pid :~p ~n",[_Pid]), %% debug info
	{noreply, State};
handle_call({fetch_node_resource,Content}, From, State) ->
	io:format("Recieve fetch_node_resource Req ~p ~p ~p ~n",[Content,From,State] ), %% debug info
	_Pid = spawn(fetch_node_resource_cmd,handle,[Content,From,State]),
	{noreply, State};  
handle_call({get_node_status,Content}, From, State) ->
	io:format("Recieve get_node_status Req ~p ~p ~p ~n",[Content,From,State] ), %% debug info
	_Pid = spawn(get_node_status_cmd,handle,[Content,From,State]),
	{noreply, State};  
handle_call({start_node_controller,Content}, From, State) ->
	io:format("Recieve start_node_controller Req ~p ~p ~p ~n",[Content,From,State] ), %% debug info
	_Pid = spawn(start_node_controller_cmd,handle,[Content,From,State]),
	{noreply, State};
handle_call({start_vm,Content},From,State)->
	io:format("Recieve start_vm Req ~p ~p ~p ~n",[Content,From,State] ), %% debug info
	_Pid = spawn(start_vm_cmd,handle,[Content,From,State]),
	{noreply, State};
handle_call({stop_vm,Content},From,State)->
	io:format("Recieve stop_vm Req ~p ~p ~p ~n",[Content,From,State] ), %% debug info
	_Pid = spawn(stop_vm_cmd,handle,[Content,From,State]),
	{noreply, State};
handle_call({create_vm,Content},From,State)->
	io:format("Recieve create_vm Req ~p ~p ~p ~n",[Content,From,State] ), %% debug info
	_Pid = spawn(create_vm_cmd,handle,[Content,From,State]),
	{noreply, State};
handle_call({destory_vm,Content},From,State)->
	io:format("Recieve destory_vm Req ~p ~p ~p ~n",[Content,From,State] ), %% debug info
	_Pid = spawn(destory_vm_cmd,handle,[Content,From,State]),
	{noreply, State}.

%% --------------------------------------------------------------------
%% Function: handle_cast/2
%% Description: Handling cast messages
%% Returns: {noreply, State}          |
%%          {noreply, State, Timeout} |
%%          {stop, Reason, State}            (terminate/2 is called)
%% --------------------------------------------------------------------
handle_cast(Msg, State) ->
    {noreply, State}.

%% --------------------------------------------------------------------
%% Function: handle_info/2
%% Description: Handling all non call/cast messages
%% Returns: {noreply, State}          |
%%          {noreply, State, Timeout} |
%%          {stop, Reason, State}            (terminate/2 is called)
%% --------------------------------------------------------------------

handle_info({nodeup, Node}, State) ->
	io:format("node up !!"),
	%% restart node controller 
	case rpc:call(Node,application,start,[node_controller],1000) of
		 {badrpc,{Reason,_}} when Reason == already_started ->
					io:format("node come back ~p ~n",[Node]);
		 {badrpc,Reason}  ->
					io:format("start node controller fail ! reason ~p ~n",[Reason]);
	     Value -> Value
    end,
	%% modify node status
	%meta_data_ops:open_node_resource(Node),
    {noreply, State};

handle_info({nodedown, Node}, State) ->
	io:format("node down !!"),
    %% modify node status
    meta_data_ops:open_node_resource(Node),
	%% start a ping to connect period node.
	Pid = spawn_link(fun()->nodeup_monitor(Node) end),
    %% record node & pid pair 
	List = State#state.nodeup_monitor_info_list,
	NewState = State#state{nodeup_monitor_info_list=[{Node,Pid}|List]},
    {noreply, NewState};

handle_info({'EXIT', Pid, Reason}, State) ->
	List = State#state.nodeup_monitor_info_list,
    NewState = State#state{nodeup_monitor_info_list=lists:keydelete(Pid,2,List)},
	{noreply, NewState}.

%% --------------------------------------------------------------------
%% Function: terminate/2
%% Description: Shutdown the server
%% Returns: any (ignored by gen_server)
%% --------------------------------------------------------------------
terminate(Reason, State) ->
    ok.

%% --------------------------------------------------------------------
%% Func: code_change/3
%% Purpose: Convert process state when code is changed
%% Returns: {ok, NewState}
%% --------------------------------------------------------------------
code_change(OldVsn, State, Extra) ->
    {ok, State}.

%% --------------------------------------------------------------------
%%% Internal functions
%% --------------------------------------------------------------------

init_mnesia() ->
    mnesia:start(),
	case catch mnesia:table_info(counter,where_to_read) of
 	{'EXIT', _} -> %% non-existing table
         case sequence:init() of
			   {aborted, Reason} ->
				  {aborted, Reason};
         	   _ -> 
				  io:format("create table over ~n")
			   end;
	Node ->
 		io:format("** Table counter already exists on ~p, just entering data~n", [Node])
    end,
	
	case catch mnesia:table_info(network_address_resource,where_to_read) of
 	{'EXIT', _} -> %% non-existing table
         case meta_data_ops:create_network_address_resource_table() of
			   {aborted, Reason2} ->
				  {aborted, Reason2};
         	   _ -> 
				  io:format("create table over ~n")
			   end;
	Node2 ->
 		io:format("** Table ~w already exists on ~p, just entering data~n", [network_address_resource, Node2])
    end,
	case catch mnesia:table_info(node_resource,where_to_read) of
 	{'EXIT', _} -> %% non-existing table
         case meta_data_ops:create_node_resource_table() of
			   {aborted, Reason3} ->
				  {aborted, Reason3};
         	   _ -> 
				  io:format("create table over ~n")
			   end;
	Node3 ->
 		io:format("** Table node_resource already exists on ~p, just entering data~n", [Node3])
    end,
	case catch mnesia:table_info(vm_resource_config,where_to_read) of
 	{'EXIT', _} -> %% non-existing table
         case meta_data_ops:create_vm_resource_config_table() of
			   {aborted, Reason4} ->
				  {aborted, Reason4};
         	   _ -> 
				  io:format("create table over ~n")
			   end;
	Node4 ->
 		io:format("** Table vm_resource_config already exists on ~p, just entering data~n", [Node4])
    end,
	case catch mnesia:table_info(vm_runtime_info,where_to_read) of
 	{'EXIT', _} -> %% non-existing table
         case meta_data_ops:create_vm_runtime_info_table() of
			   {aborted, Reason5} ->
				  {aborted, Reason5};
         	   _ -> 
				  io:format("create table over ~n")
			   end;
	Node5 ->
 		io:format("** Table vm_runtime_info already exists on ~p, just entering data~n", [Node5])
    end,
	mnesia:start(),
    ok = mnesia:wait_for_tables([node_resource,network_address_resource,vm_resource_config,vm_runtime_info,counter], 2000).

monitor_nodes()->
	case net_kernel:monitor_nodes(true) of 
		ok -> ok;
		Error -> 
			io:format("unable to start monitor") ,
			error
	end. 

nodeup_monitor(Node) ->
     	 case net_adm:ping(Node) of
		 	pong ->
			 	io:format("node up !! ~n"),
				ok ;
		 	pang ->
			 	io:format("node still down ~n"),
				receive
					exit ->
						void
					after 3000 ->
						nodeup_monitor(Node)
				end
		 end.

